package demo.terrain;

import org.lwjgl.input.Keyboard;

import engine.gameObject.GameObject;
import engine.gameObject.collider.SphereCollider;
import engine.gameObject.terrain.Terrain;

public class Character extends GameObject {

	public static final float INITIAL_SPEED = 0.005f;
	public static final float GRAVITY = 0.00004f;
	
	private float fallSpeed = 0;
	private float timeForJump;
	
	private boolean forward;
	private boolean backward;
	private boolean left;
	private boolean right;
	
	private TerrainCamera camera;
	
	private float speed;
	
	private float[] oldPos;
	
	public Character() {
		setRigidbody(true);
		setBoundingRadius(0.5f);
		setCollider(new SphereCollider());
		
		speed = INITIAL_SPEED;
		
		oldPos = new float[3];
	}
	
	@Override
	public void init() {
		camera = (TerrainCamera)getScene().getCurrentCamera();
	}

	@Override
	public void update(float timeElapsed) {
		oldPos[0] = getPosX();
		oldPos[1] = getPosY();
		oldPos[2] = getPosZ();
		
		//fix lags when resizing screen
		timeElapsed = Math.min(50, timeElapsed);
		
		gravity(timeElapsed);
		
		move(timeElapsed);
	}
	
	private void move(float timeElapsed) {
		//update walk position
		if(forward) {
			moveCamera(speed * timeElapsed);
		}
		
		if(backward) {
			moveCamera(-speed * timeElapsed);
		}
		
		if(left) {
			strafeCamera(speed * timeElapsed);
		}
		
		if(right) {
			strafeCamera(-speed * timeElapsed);
		}
	}
	
	/**
	 * This moves the camera to the left (if move is positive) or to the right (if move is negative). 
	 * 
	 * @param move
	 */
	private void strafeCamera(float move) {
		float angle = camera.getAngleFloor() + (float)Math.PI / 2;
		translate((float)Math.sin(angle) * move, 0, (float)Math.cos(angle) * move);
	}
	
	/**
	 * Moves the camera to the front (if move is positive) or to the back (if move is negative). 
	 * @param move
	 */
	private void moveCamera(float move) {
		translate((float)Math.sin(camera.getAngleFloor()) * move, 0, (float)Math.cos(camera.getAngleFloor()) * move);
	}
	
	private void gravity(float timeElapsed) {
		//consider gravity
		fallSpeed -= GRAVITY * timeElapsed;
		translate(0, fallSpeed * timeElapsed, 0);
		timeForJump -= timeElapsed;
	}
	
	@Override
	public void collides(GameObject other, float[] upVector) {
		if(other instanceof Terrain) {
			translate(upVector);
			fallSpeed = 0;
			timeForJump = 150;
		} else {
			setPosition(oldPos);
			fallSpeed = 0;
		}
	}
	
	/**
	 * Must be called, any time a key is pressed. 
	 * @param key
	 */
	public void keyPressed(int key) {
		switch (key) {
		case Keyboard.KEY_W:
		case Keyboard.KEY_UP:
			forward = true;
			break;
		case Keyboard.KEY_A:
		case Keyboard.KEY_LEFT:
			left = true;
			break;
		case Keyboard.KEY_S:
		case Keyboard.KEY_DOWN:
			backward = true;
			break;
		case Keyboard.KEY_D:
		case Keyboard.KEY_RIGHT:
			right = true;
			break;
		case Keyboard.KEY_SPACE: 
			if(timeForJump > 0) {
				fallSpeed = 0.01f;
				timeForJump = -1;
			}
		}
	}
	
	/**
	 * Must be called, any time a key is released. 
	 * 
	 * @param key
	 */
	public void keyReleased(int key) {
		switch (key) {
		case Keyboard.KEY_W:
		case Keyboard.KEY_UP:
			forward = false;
			break;
		case Keyboard.KEY_A:
		case Keyboard.KEY_LEFT:
			left = false;
			break;
		case Keyboard.KEY_S:
		case Keyboard.KEY_DOWN:
			backward = false;
			break;
		case Keyboard.KEY_D:
		case Keyboard.KEY_RIGHT:
			right = false;
			break;
		}
	}
}
